package com.gitee.slowcreator.http.executor.hutool;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.io.resource.BytesResource;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import com.gitee.slowcreator.http.annotation.HttpClient;
import com.gitee.slowcreator.http.enums.RequestType;
import com.gitee.slowcreator.http.exception.HttpRequestException;
import com.gitee.slowcreator.http.executor.HttpExecutor;
import com.gitee.slowcreator.http.params.ScMultipart;
import com.gitee.slowcreator.http.params.ScReturn;
import com.gitee.slowcreator.http.params.ScUnionParams;
import com.gitee.slowcreator.http.util.JacksonUtil;
import com.gitee.slowcreator.http.util.UrlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * hutool http 执行器
 *
 * @author zxw
 * @date 2023/1/6 下午9:22
 */
@Slf4j
public class HutoolHttpExecutor implements HttpExecutor {

    @Override
    public ScReturn doHttpRequest(HttpClient httpClient, ScUnionParams scUnionParams) {

        // 最后执行会使用 ScUnionParams 的 httpUrl 参数 如果为空 使用注解的url赋值过去
        if (StrUtil.isBlank(scUnionParams.getHttpUrl())) {
            scUnionParams.setHttpUrl(httpClient.url());
        }

        // 请求的http方式转换为 hutool 支持的请求方式参数
        Method httpMethod = getHttpMethod(httpClient.requestType());
        HttpRequest httpRequest = HttpRequest
                .of(
                        UrlUtil.rebuildUrl(scUnionParams.getHttpUrl(), scUnionParams.getHttpParams()),
                        Charset.forName(httpClient.charset())
                )
                .method(httpMethod);

        httpRequest.contentType(StrUtil.format(httpClient.contentType(), httpClient.charset()));

        boolean multipart = httpClient.isMultipart();
        if (multipart) {
            // 文件类型额外处理
            multipartExtraProcessing(scUnionParams, httpRequest);
        }

        if (httpClient.requestType() != RequestType.GET) {

            processingBody(httpClient, scUnionParams, httpRequest);
        }

        if (CollectionUtil.isNotEmpty(scUnionParams.getHttpHeader())) {
            // 添加 header 参数
            httpRequest.headerMap(scUnionParams.getHttpHeader(), true);
        }

        int timeout = httpClient.timeout();
        // 超时时间小于-1 设置为-1 即无超时时间
        timeout = Math.max(timeout, -1);
        httpRequest.timeout(timeout);
        ScReturn scReturn = new ScReturn();
        try (HttpResponse execute = httpRequest.execute(httpClient.isAsync())) {
            // 是否为下载请求
            boolean download = httpClient.download();

            if (download) {
                // 下载
                downloadFile(httpClient, execute, scReturn);
            } else {
                // 非下载请求获取返回数据
                boolean ok = execute.isOk();
                byte[] bytes = execute.bodyBytes();
                scReturn.setData(bytes);
                scReturn.setSuccess(ok);
            }
            // 设置返回头部信息 如果需要
            getReturnHeader(scUnionParams, execute, scReturn);
            // 获取返回 Content-Type
            getContentType(execute, scReturn);
        }

        return scReturn;
    }

    /**
     * 获取返回 Content-Type
     *
     * @param execute
     * @param scReturn
     */
    private static void getContentType(HttpResponse execute, ScReturn scReturn) {
        String contentType = execute.header("Content-Type");
        if (StringUtils.isBlank(contentType)) {
            contentType = execute.header("content-type");
        }
        scReturn.setContentType(contentType);
    }

    /**
     * 获取返回头部信息
     *
     * @param scUnionParams
     * @param execute
     * @param scReturn
     */
    private static void getReturnHeader(ScUnionParams scUnionParams, HttpResponse execute, ScReturn scReturn) {
        Set<String> returnHeader = scUnionParams.getReturnHeader();
        Map<String, String> retH = new HashMap<>();
        scReturn.setReturnHeader(retH);
        if (CollectionUtil.isNotEmpty(returnHeader)) {
            for (String header : returnHeader) {
                String h = execute.header(header);
                retH.put(header, h);
            }
        }
    }

    /**
     * 下载文件
     *
     * @param httpClient
     * @param execute
     * @param scReturn
     */
    private static void downloadFile(HttpClient httpClient, HttpResponse execute, ScReturn scReturn) {

        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        boolean downloadLocal = httpClient.downloadLocal();
        String downloadLocalFilename = httpClient.downloadLocalFilename();
        StreamProgress streamProgress = new StreamProgress() {
            @Override
            public void start() {
                log.info("文件开始下载");
            }

            @Override
            public void progress(long l, long l1) {
                log.info("文件已下载:{}%", (l1 / l) * 100);
            }

            @Override
            public void finish() {
                // 只有非保存本地才设置data 保存本地的data全程都为空
                if (!downloadLocal) {
                    scReturn.setData(os.toByteArray());
                }
            }
        };


        if (downloadLocal) {
            // 写入文件到本地
            execute.writeBody(new File(downloadLocalFilename), streamProgress);
        } else {
            // 写入数据到 输出流
            execute.writeBody(os, false, streamProgress);
        }
    }

    /**
     * 处理body参数（非get请求）
     *
     * @param httpClient
     * @param scUnionParams
     * @param httpRequest
     */
    private static void processingBody(HttpClient httpClient, ScUnionParams scUnionParams, HttpRequest httpRequest) {
        Map<String, Object> httpBody = scUnionParams.getHttpBody();
        if (CollectionUtil.isNotEmpty(httpBody)) {
            if (StrUtil.contains(httpClient.contentType(), "application/json")) {
                // json 请求
                httpRequest.body(JacksonUtil.toJsonString(httpBody));

                httpRequest.setUrl(UrlUtil.rebuildUrl(httpClient.url(), scUnionParams.getHttpParams()));
            } else {
                httpRequest.form(httpBody);
            }
        }
    }

    /**
     * 文件类型额外处理
     *
     * @param scUnionParams 请求参数
     * @param httpRequest   http 请求
     */
    private static void multipartExtraProcessing(ScUnionParams scUnionParams, HttpRequest httpRequest) {
        ScMultipart scMultipartParams = scUnionParams.getScMultipart();
        byte[] multipartData = scMultipartParams.getData();
        String fileName = scMultipartParams.getFileName();
        if (ArrayUtil.isEmpty(multipartData)) {
            throw new HttpRequestException("multipart 请求，文件不能为空！");
        }
        if (StrUtil.isBlank(fileName)) {
            throw new HttpRequestException("multipart 请求，文件名不能为空！");
        }

        String fileFormName = scMultipartParams.getFileFormName();
        fileFormName = StrUtil.isBlank(fileFormName) ? "file" : fileFormName;
        httpRequest.form(fileFormName, new BytesResource(multipartData, fileName));
    }

    /**
     * 获取http请求方法
     *
     * @param requestType
     * @return
     */
    private static Method getHttpMethod(RequestType requestType) {

        if (requestType == null) {
            throw new IllegalArgumentException("请求方式不能为空！");
        }

        Method method;
        switch (requestType) {
            case GET:
                method = Method.GET;
                break;
            case POST:
                method = Method.POST;
                break;
            case PUT:
                method = Method.PUT;
                break;
            case DELETE:
                method = Method.DELETE;
                break;
            default:
                throw new HttpRequestException("不支持的请求方式！");
        }

        return method;
    }
}
